package server;
import gestion_bd.ConectorBD;
import gestion_bd.FingerprintCalculator;

import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.Map;

import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLSocket;
import javax.sql.rowset.spi.SyncResolver;

import common.Mensaje;

/**
 * Clase principal del servidor. Es quien mantiene el estado actual en el que se encuentra el servidor y puede acceder a los distintos m�dulos
 * que lo componen. Se mantiene en ejecuci�n en el thread principal y crea los restantes hilos que interactuan con los dem�s componentes del
 * sistema. Se encarga de efecutar los controles que sean necsarios para mantener la consistencia entre los distintos dispositivos conectados 
 * a �l. Adem�s cuenta con acceso a la base de datos para registrar o consultar informaci�n.
 * @author Matias
 *
 */
public class ServidorPrincipal {
	
	private int puertoClientes;
	private int puertoAdmin;
	private int puertoConsultas;
	
	private static final int TIME_OUT_VOTACION =90000;// time out para maximo tiempo de votaci�n
	private static final int TIME_OUT_QUORUM_INICIAL=30000;
	
	private int estado_actual;
	
	private SSLServerSocket socket_servidor_android;
	private SSLServerSocket socket_servidor_admin;
	
	private SSLSocket conexion_admin;
	
	private boolean fin_de_session= false;
	
	private boolean admitiendo_clientes=true;
	
	private Map<String, ThreadServerAndroid> mapeoClientesAndroid;
	private FingerprintCalculator finger;
	private ConectorBD conectorbd;
	
	private ThreadServerAdmin thread_admin;
	
	private ServiciosSSL serviciosSSL;
	
	private String temaActual;
	private int idSesionActual;
	private int idTemaActual;
	
	protected boolean hayRtdos=false;
	
	private static final int POSITIVOS = 0;
	private static final int NEGATIVOS = 1;
	private static final int ABSTENCION = 2;
	
	private boolean e5_pasar_sig_estado;
	private boolean e4_pasar_sig_estado;
	
	private int quorum;
	
	private Log log;
	
	/**
	 * Constructor 1 de la clase.
	 * @param ptoConsultas : entero con el puerto donde se recibiran las consultas a la base de datos.
	 * @param ptoAdmin : entero con el puerto donde se escuchar� al cliente admin.
	 * @param ptoClientes : : entero con el puerto donde se recibiran los clientes android
	 */
	public ServidorPrincipal(int ptoConsultas, int ptoAdmin, int ptoClientes){
		
		log = new Log();
		
		puertoConsultas = ptoConsultas;
		puertoAdmin = ptoAdmin;
		puertoClientes = ptoClientes;
		
		estado_actual=0;
		fin_de_session=false;
		//System.err.println(Thread.getAllStackTraces().keySet().size());
		conectorbd = ConectorBD.getInstancia();
		
		try {
			conectorbd.conectar("root", "");
		
			mapeoClientesAndroid = new Hashtable<String, ThreadServerAndroid>(50);
			finger = new FingerprintCalculator();
			temaActual=null;
			serviciosSSL = new ServiciosSSL();
			//serviciosSSL.iniciar_keystores("123456".toCharArray(), "abcdefg".toCharArray()); // <<<<<<<<<<<<<<<<<<<<< QUE LOS RECIBA COMO PARAMETROS!!!!!!!!!!
			serviciosSSL.iniciar_keystores("123456".toCharArray(), "abcdefg".toCharArray()); // (a no asustarse, este constructor es solo para testeo)
			iniciar_servidor();
		} catch (SQLException e) {
			
			//System.err.println("No se pudo efectuar la conexi�n a la Base de datos.\nAsegurese de que el servidro MySQL este corriendo\nDetalles:"+e.getMessage());
			String msj="No se pudo efectuar la conexi�n a la Base de datos.\nAsegurese de que el servidro MySQL este corriendo\nDetalles:"+e.getMessage();
			log.writeLog(Log.WARNING, "SERVIDOR", "Conexion BD", msj);
		}
	}
	
	//segundo constructor recibiendo el valor del quorum
	/**
	 * Constructor 1 de la clase.
	 * @param ptoConsultas : entero con el puerto donde se recibiran las consultas a la base de datos.
	 * @param ptoAdmin : entero con el puerto donde se escuchar� al cliente admin.
	 * @param ptoClientes : : entero con el puerto donde se recibiran los clientes android
	 * @param q : entero con el nro necesario para el quorum.
	 * @param pass : cadena de car�cteres con el password del jks [es el mismo que el de la clave]
	 */
	public ServidorPrincipal(int ptoConsultas, int ptoAdmin, int ptoClientes, int q, char[] pass){
		log = new Log();
		
		quorum=q;
		puertoConsultas = ptoConsultas;
		puertoAdmin = ptoAdmin;
		puertoClientes = ptoClientes;
		
		estado_actual=0;
		fin_de_session=false;
		//System.err.println(Thread.getAllStackTraces().keySet().size());
		conectorbd = ConectorBD.getInstancia();
		
		try {
			conectorbd.conectar("root", "");// [$PELIGRO conecta como root a la bd]
		
			mapeoClientesAndroid = new Hashtable<String, ThreadServerAndroid>(50);
			finger = new FingerprintCalculator();
			temaActual=null;
			serviciosSSL = new ServiciosSSL();
			//serviciosSSL.iniciar_keystores("123456".toCharArray(), "abcdefg".toCharArray()); // <<<<<<<<<<<<<<<<<<<<< QUE LOS RECIBA COMO PARAMETROS!!!!!!!!!!
			char[] passKey = (pass.length<7)? pass: Arrays.copyOfRange(pass, 0, 7);
			serviciosSSL.iniciar_keystores(pass, passKey); // <<<<<<<<<<<<<<<<<<<<< QUE LOS RECIBA COMO PARAMETROS!!!!!!!!!!
			iniciar_servidor();
		} catch (SQLException e) {
			//e.printStackTrace();
			//System.err.println("No se pudo efectuar la conexi�n a la Base de datos.\nAsegurese de que el servidro MySQL este corriendo\nDetalles:"+e.getMessage());
			String msj = "No se pudo efectuar la conexi�n a la Base de datos.\nAsegurese de que el servidro MySQL este corriendo\nDetalles:"+e.getMessage();
			log.writeLog(Log.WARNING, "SERVIDOR", "Conexi�n BD", msj);
		}
	}

	/**
	 * M�todo con el bucle principal del servidor.
	 */
	public void iniciar_servidor(){
		while(!fin_de_session){
			switch (estado_actual){
				case 0:{ estado0(); break; }
				case 1:{ estado1(); break; }
				case 2:{ estado2(); break; }
				case 3:{ estado3(); break; }
				case 4:{ estado4(); break; }
				case 5:{ estado5(); break; }
				case 6:{ estado6(); break; }
				case 7:{ estado7(); break; }
				case 8:{ estado8(); break; }
			}
			
		}
	}
		   
	private void estado0(){// este est� bien
		final ServidorPrincipal server_ppal = this;
		log.writeLog(Log.INFO, "SERVIDOR", "INICIO", "Servidor comienza su ejecuci�n");
		
		// creaci�n y ejecuci�n del thread utilizado para las consultas.
		Runnable runnable_consulta = new Runnable() {
			public void run() {
				try {
						
					SSLServerSocket socket_servidor_consulta = serviciosSSL.generarServidor(puertoConsultas);
					SSLSocket socket_consulta = (SSLSocket) socket_servidor_consulta.accept();
					serviciosSSL.printSocketInfo(socket_consulta);
					ThreadConsultas thread_consultas = new ThreadConsultas(socket_consulta, log);
					thread_consultas.atender_consultas();
					
				} catch (SSLPeerUnverifiedException e) {
						//System.err.println("No pudo verificarse la identidad del cliente admin: "+e.getMessage());
						String msj = "No pudo verificarse la identidad del cliente admin: "+e.getMessage();
						log.writeLog(Log.WARNING, "SERVIDOR", "Estado 0", msj);
						//e.printStackTrace();
						try {
							conexion_admin.close();
							socket_servidor_admin.close();
						} catch (IOException e1) {
							System.err.println(e1.getMessage());
							//e1.printStackTrace(); 
						}
				} catch (IOException e) {
					//e.printStackTrace();
					System.err.println(e.getMessage());
				}
					
			}
		};
		Thread thread_consulta = new Thread(runnable_consulta);
		thread_consulta.start();
		
		
		// creaci�n y ejecuci�n del thread que atiende al cliente admin.
		Runnable runnable = new Runnable() {
			public void run() {
				try {
						
					socket_servidor_admin = serviciosSSL.generarServidor(puertoAdmin);
					conexion_admin = (SSLSocket) socket_servidor_admin.accept();
					serviciosSSL.printSocketInfo(conexion_admin);
					
					thread_admin = new ThreadServerAdmin(conexion_admin, server_ppal, log);
					thread_admin.atender_admin();
					
				
					
				} catch (SSLPeerUnverifiedException e) {
						//System.err.println("No pudo verificarse la identidad del cliente admin: "+e.getMessage());
						String msj = "No pudo verificarse la identidad del cliente admin: "+e.getMessage();
						log.writeLog(Log.WARNING, "SERVIDOR", "Conexi�n cliente Admin", msj);
						//e.printStackTrace();
						//System.err.println(e.getMessage());
						try {
							conexion_admin.close();
							socket_servidor_admin.close();
						} catch (IOException e1) { 
							//e1.printStackTrace();
							System.err.println(e.getMessage());
						}
				} catch (IOException e) {
					//e.printStackTrace();
					System.err.println(e.getMessage());
				}
					
			}
		};
		Thread thread1 = new Thread(runnable);
		thread1.start();
		 
		
		// iniciar el thread que acepta conexiones para los clientes android
		Runnable runnable2 = new Runnable() {
			public void run() {
				try{
					socket_servidor_android= serviciosSSL.generarServidor(puertoClientes);
					
					// aceptando clientes
					while(admitiendo_clientes){
						SSLSocket conexion_entrante = (SSLSocket) socket_servidor_android.accept();
						serviciosSSL.printSocketInfo(conexion_entrante);
						
						String fingerprint = finger.obtenerFingerprint(conexion_entrante.getSession().getPeerCertificates()[0]);
						
						
						if(cliente_habilitado_a_conectarse(fingerprint)){ // verifico que no haya otro cliente conectado con el mismo fingerprint y que est� habilitado a conectarse
						
							// creaci�n del thread que atender� a ese cliente en particular
							ThreadServerAndroid nuevo_cliente = new ThreadServerAndroid(conexion_entrante, server_ppal, fingerprint, log);
							//System.out.println("CLIENTE ANDROID CONECTADO FINGERPRINT: "+fingerprint);
							log.writeLog(Log.INFO, "SERVIDOR", "Conexi�n Cliente Android", "Cliente Android Conectado Fingerprint: "+fingerprint);
							mapeoClientesAndroid.put(fingerprint, nuevo_cliente);
							nuevo_cliente.start();
							
						}else{ // fingerprint duplicado
							//System.err.println("SERVIDOR: no se permiti� la conexi�n al cliente "+fingerprint);
							String msj = "no se permiti� la conexi�n al cliente "+fingerprint;
							log.writeLog(Log.WARNING, "SERVIDOR", "Conexi�n cliente Android", msj);
							conexion_entrante.close();
						}
						
					}
				}catch(Exception e){
					//System.err.println("Se cerr� serverSocket que atendia clientes android : "+e.getMessage());
					String msj = "Se cerr� serverSocket que atendia clientes android : "+e.getMessage();
					log.writeLog(Log.INFO, "SERVIDOR", "Conexi�n cliente Android", msj);
				}
			}
		};
		Thread mythread2 = new Thread(runnable2);
		mythread2.start();
		 
		estado_actual=1;
		}
		   
	private void estado1(){// este est� bien
		// se est� esperando por inicio de sesi�n por parte del admin
	}
	
	private void estado2(){
		// inici� la sesi�n
		// sleep de 15 min para dara tiempo a que se produzca quor�m
		//System.out.println("Sleep de ppal para aceptar conexiones");
		/* Esto lo eliminamos...
		try {
			Thread.sleep(TIME_OUT_QUORUM_INICIAL); // 30 segudos, luego cambiarlo a 15 minutos 900000 milisegundos
		} catch (InterruptedException e) {
			System.err.println(e.getMessage());
		}
		*/
		if(admitiendo_clientes){ // i.e. si ya deshabilitó las nuevas conexiones entrantes 
			// dejo el thread en espera hasta que admin los despierte (cuando envíe msj para dejar de admitir clientes)
			synchronized(this){
				try {
					this.wait();
				} catch (InterruptedException e) {System.out.println(e.getMessage());}
			}
			
			// pasaron los 15min, dejo de aceptar conexiones de clientes android.
			admitiendo_clientes = false;
			try {
				socket_servidor_android.close();
			} catch (IOException e1) {System.err.println(e1.getMessage());}
			
			// se registran todos los clientes conectados en la BD
			for(ThreadServerAndroid tsa: mapeoClientesAndroid.values()){
				registrarClienteConectado(tsa.obtenerFingerprint());
			}
			//despierto a admin para que controle si hay qu�rum
			
			//System.out.println("SERVER PPAL: Se dejarn de admitir conexiones");
		
			log.writeLog(Log.INFO, "SERVIDOR", "ESTADO 2", "Se dejarn de admitir conexiones");
		}
		/*
		synchronized (this) {// espero a que admin pregunte por quorum
			try {
				this.wait();
			} catch (InterruptedException e) {System.out.println(e.getMessage());}
		}
		// creo que esto está de más, se hace en admin- SEPT2014
		if(hayQuorum())
			estado_actual=3;
		else
			estado_actual= 8;
			*/
		
	}
		   
	private void estado3(){
		if(verificarTodosNotificados()){
			synchronized(thread_admin){
				thread_admin.notify();
			}
			
		}
	}
		   
	@SuppressWarnings("deprecation")
	private void estado4(){
		//System.out.println("Hay tema: "+temaActual);
		String msj = "Hay tema: "+temaActual;
		log.writeLog(log.INFO, "SERVIDOR", "Estado 4", msj);
		
		hayRtdos=false;
		
		// por precaucion se crea un thread con un timeout para que luego de un cierto tiempo despierte al thread de ppal
		 e4_pasar_sig_estado = false;
		final ServidorPrincipal server = this;
		// thread que lleva la cuenta del timeout (esto nos salvo durante la demo!!!)
		Runnable timeout_run = new Runnable(){
			public void run(){
				try {
					// sleep time out
					Thread.sleep(30000); // ###### ponerlo como constante!!!!!
					
					synchronized (server) {
						// cambiar condicion
						e4_pasar_sig_estado=true;  // habr� problema con atomicidad? (no, justamente est� dentro de un "synchronized". salame!)
						
						// despierto al thread del server
						server.notify();
					}
				} catch (InterruptedException e) {
					System.err.println(e.getMessage());
				}
			}
		};
		Thread timeout_thread = new Thread(timeout_run);
		timeout_thread.start();
		
		e4_pasar_sig_estado=verificarTodosNotificadosE4();
		
		synchronized(this){
			//System.out.println("----------------FUERON TODOS NOTIFICADOS1: "+e4_pasar_sig_estado);
			while(!e4_pasar_sig_estado){
				try {
					
					this.wait(); // bloqueado hasta que recibe un voto de un cliente o timeout_thread lo despierta
					
					if(!e4_pasar_sig_estado){ // luego del wait la condicion podria cambiar
						
						e4_pasar_sig_estado= verificarTodosNotificadosE4();
					}
					
				} catch (InterruptedException e) {
					//e.printStackTrace();
					System.err.println(e.getMessage());
				}
			}
		}
		
		// si el thread del timeout a�n est� corriendo, lo detengo (se destruye as�?)
		if(timeout_thread.isAlive())
			timeout_thread.stop(); 
		
		// verificar si hay quorum para ver si pasa a votaci�n [OBS: el admin no deber�a de enviar msj de "INICIO VOTACION"???] 
		if(hayQuorum()){
			//System.out.println("Server cambia a estado 5");
			log.writeLog(Log.INFO, "SERVIDOR", "Estado 4", "Cambia a estado 5");
			estado_actual=5;
		}else{
			//System.out.println("No hay quorum, Server cambia a estado 7");
			log.writeLog(Log.INFO, "SERVIDOR", "Estado 4", "Cambia a estado 7");
			estado_actual=7;
		}
		
	}

	@SuppressWarnings("deprecation")
	private void estado5(){
		e5_pasar_sig_estado = false; // cambiarlo
		
		final ServidorPrincipal server = this;
		// thread que lleva la cuenta del timeout
		Runnable timeout_run = new Runnable(){
			public void run(){
				try {
					// sleep time out
					Thread.sleep(TIME_OUT_VOTACION);
					
					synchronized (server) {
						// cambiar condicion
						e5_pasar_sig_estado=true;  // habr� problema con atomicidad?
						
						// despierto al thread del server
						server.notify();
					}
					
					
				} catch (InterruptedException e) {
					System.err.println(e.getMessage());
				}
			}
		};
		Thread timeout_thread = new Thread(timeout_run);
		timeout_thread.start();
		
		synchronized(this){
			while(!e5_pasar_sig_estado){
				try {
					
					this.wait(); // bloqueado hasta que recibe un voto de un cliente o timeout_thread lo despierta
					if(!e5_pasar_sig_estado){ // luego del wait la condicion podria cambiar
						e5_pasar_sig_estado= verificarTodosVotaron1();
						
					}
					
				} catch (InterruptedException e) {
					//e.printStackTrace();
					System.err.println(e.getMessage());
				}
			}
		}
		
		// si el thread del timeout a�n est� corriendo, lo detengo (se destruye as�?)
		if(timeout_thread.isAlive())
			timeout_thread.stop();  // che, esto est� bien???
		
		// cambio al prox estado
		estado_actual=6;
		//System.out.println("SERVIDOR PPAL: pasa a estado 6");
		log.writeLog(Log.INFO, "SERVIDOR", "Estado 5", "pasa a estado 6");
	}



	private void estado6(){
		// procesando los rtdos de la votaci�n
		//System.out.println("SERVIDOR PPAL: Procesando rtdos");
		log.writeLog(Log.INFO, "SERVIDOR", "ESTADO 6", "Procesando rtdos");

		
		int[] rtdos = procesarRtdos();
		String rtdo = "Positivos: "+rtdos[POSITIVOS]+", Negativos: "+rtdos[NEGATIVOS]+", Abstenciones: "+rtdos[ABSTENCION]+".";
		//System.out.println("SERVIDOR PPAL: resultados "+rtdo);
		log.writeLog(Log.INFO, "SERVIDOR", "ESTADO 6", rtdo);
		registrarRtdo(rtdo);
		
		hayRtdos=true;
		
		//despierto a admin para que pida los rtdos
		synchronized (thread_admin) {
			thread_admin.notify();
		}
		
		
		synchronized (this) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				//e.printStackTrace();
				System.err.println(e.getMessage());
			}
		}
		estado_actual=7;
		//System.out.println("Server cambia a estado 7");
		log.writeLog(Log.INFO, "SERVIDOR", "Estado 6", "Server cambia a estado 7");
	}

	private void estado7(){
		// admin decide si pasa a nuevo tema o cierra sesi�n.
	}
		   
	private void estado8(){
		try {
			Thread.sleep(10000); // ### usar constante para este timeout
		} catch (InterruptedException e) {
			System.err.println(e.getMessage());
		}
		//System.out.println("SERVER PPAL: CERRANDO SESION");
		log.writeLog(Log.INFO, "SERVIDOR", "Estado 8", "Cerrando sesi�n");
		fin_de_session=true;
		
		try {
			conectorbd.cerrarConsulta();
		} catch (SQLException e) {
			System.err.println(e.getMessage());
			
		}
	}
	
	/**
	 * M�todo setter para establecer el estado del servidor (usar con cuidado)
	 * @param i : entero con el nro de estado
	 */
	public void set_estado(int i){
		estado_actual=i;
	}
		   
	/**
	 * M�todo getter que devuelve si hubo fin de sesi�n o no
	 * @return true si finaliz� la sesi�n, caso contrario false
	 */
	public boolean get_fin_sesion(){
		return fin_de_session;
	}
	
	/**
	 * M�todo getter que devuelve el estado actual en el que se encuentra el servidor
	 * @return entero con el estado actual
	 */		   
	public int get_estado(){
		return estado_actual;
	}

	/**
	 * M�todo que realiza la verificaci�n del quorum.
	 * @return true si hay quorum, false caso contrario
	 */
	protected boolean hayQuorum(){
		// ac� se verifica que la cantidad de clientes conectados sea mayor a la mitad +1
		// por ahora solo verificaremos que haya 1
		int i=0;
		for(ThreadServerAndroid thread: mapeoClientesAndroid.values()){
			if(thread.clienteEstaConectado())
				i++;
		}
		return i >= quorum;
	}
	
	/**
	 * M�todo que devuelve los resultados de la votaci�n en un string
	 * @return String con los resultados de la votaci�n
	 */
	protected String obtenerRtdos(){
		int[] rtdos = procesarRtdos();
		String rtdo = "Positivos: "+rtdos[POSITIVOS]+", Negativos: "+rtdos[NEGATIVOS]+", Abstenciones: "+rtdos[ABSTENCION]+".";
		return rtdo;
	}
	
	/**
	 * M�todo que establece el tema actual tratado en la sesi�n y lo almacena en la BD, tambi�n se mantiene en una
	 * variable de la clase.
	 * @param t : String con el tema actual
	 */
	protected void definirTemaActual(String t){
		temaActual = t;
		String sentenciaSQL = "INSERT INTO temas (tema) VALUES('"+t+"');";
		try {
			conectorbd.consulta(sentenciaSQL);
			
			sentenciaSQL = "SELECT LAST_INSERT_ID();";
			ResultSet rs = conectorbd.consulta(sentenciaSQL);
			rs.next();
			idTemaActual = rs.getInt("LAST_INSERT_ID()");
			
			sentenciaSQL = "INSERT INTO temas_por_sesion (id_sesion, id_tema) VALUES ("+idSesionActual+", "+idTemaActual+");";
			conectorbd.consulta(sentenciaSQL);
			
			
		} catch (SQLException e) {
			//e.printStackTrace();
			System.err.println(e.getMessage());
		}
	}
	
	/**
	 * Getter que devuelve el tema actual que se est� tratando.
	 * @return string con el tema actual.
	 */
	protected String obtenerTemaActual(){
		return temaActual;
	}

	/**
	 * M�todo sincronizado (para usar por threads) que recibe el voto de un cliente. Hace verificaciones (�Cuales?) y lo registra en la bd.
	 * @param voto : String con el voto del cliente.
	 * @param fingerprint_cliente : String con el fingerprint del cliente (utilizado a modo de ID)
	 * @return true si el voto fue registrado correctamente, false en caso contrario 
	 */
	protected synchronized boolean enviarVoto(String voto, String fingerprint_cliente){
		
		//System.out.println("VOTO RECIBIDO: Asambleista "+fingerprint_cliente+" emitio voto "+voto);
		String msj = "VOTO RECIBIDO: Asambleista "+fingerprint_cliente+" emitio voto "+voto;
		log.writeLog(Log.INFO, "SERVIDOR", "Recepci�n de voto", msj);
		
		boolean se_registro_voto=false;
		
		try {
			String sentenciaSQL = "SELECT id_cliente FROM votos_por_tema WHERE id_cliente = '"+fingerprint_cliente+"' AND id_sesion="+idSesionActual+" AND id_tema="+idTemaActual+";";
			ResultSet rs = conectorbd.consulta(sentenciaSQL);
			if(!rs.next()){ // si no hay un voto registrado para este votante
				sentenciaSQL = "INSERT INTO votos_por_tema (id_sesion, id_tema, id_cliente, voto) VALUES ("+idSesionActual+","+idTemaActual+",'"+fingerprint_cliente+"','"+voto+"');";
				int cantModificaciones = conectorbd.modificarBD(sentenciaSQL); // inserto el voto
				
				se_registro_voto= cantModificaciones > 0;
				
			}else{
				//System.err.println("El votante con id "+fingerprint_cliente+" ya hab�a emitido su voto para el tema '"+temaActual+"' de la sesion "+idSesionActual);
				msj = "El votante con id "+fingerprint_cliente+" ya hab�a emitido su voto para el tema '"+temaActual+"' de la sesion "+idSesionActual;
				log.writeLog(Log.WARNING, "SERVIDOR", "Registro de Voto", msj);
			}
			
		} catch (SQLException e) {
			//e.printStackTrace();
			System.err.println(e.getMessage());
		}
		//conectorbd.consulta(sentencia);
		return se_registro_voto;
	}
	
	/**
	 * Getter de la id de la sesi�n actual
	 * @return entero con la id de la sesi�n actual
	 */
	protected int getIdSesionActual(){
		return idSesionActual;
	}
	
	/**
	 * Getter de la id del tema actual
	 * @return entero con la id de la sesi�n actual
	 */	
	protected int getIdTemaActual(){
		return idTemaActual;
	}
	
	/**
	 * Setter de la id de la sesi�n actual. Lo registra en la bd y luego actualiza el atributo correspondiente dentro de esta clase.
	 */	
	protected void setSesionActual(){
		String sentenciaSQL = "INSERT INTO sesiones (fecha) VALUES(now());";
		try {
			conectorbd.consulta(sentenciaSQL);
			
			sentenciaSQL = "SELECT LAST_INSERT_ID();";
			ResultSet rs = conectorbd.consulta(sentenciaSQL);
			rs.next();
			idSesionActual = rs.getInt("LAST_INSERT_ID()");
			
		} catch (SQLException e) {
			//e.printStackTrace();
			System.err.println(e.getMessage());
		}
	}
	
	/**
	 * M�todo que registra un cliente dentro de la sesi�n actual.
	 * @param id_cliente : string con el fingerprint del cliente
	 */
	protected void registrarClienteConectado(String id_cliente){
	// verificar si anteriormente no fue registrado????
		String sentenciaSQL = "INSERT INTO clientes_por_sesion (id_sesion, id_cliente) VALUES("+idSesionActual+", '"+id_cliente+"');";
		// verificar al admitir si un cliente figura en la tabla clientes???
		try {
			conectorbd.consulta(sentenciaSQL);
			if(conectorbd.cantModificaciones()>0){
				//System.out.println("Se inserto el cliente "+id_cliente);
				log.writeLog(Log.INFO, "SERVIDOR", "Registro cliente", "Se inserto el cliente "+id_cliente);
			}else{
				//System.err.println("No se inserto el cliente "+id_cliente);
				log.writeLog(Log.WARNING, "SERVIDOR", "Registro cliente", "No se inserto el cliente "+id_cliente);
			}
		} catch (SQLException e) {
			System.err.println(e.getMessage());
		}
	}
	
	/**
	 * M�todo que registra el rtdo de una votaci�n en la bd.
	 * @param rtdo: String con el rtdo de una votaci�n
	 */
	protected void registrarRtdo(String rtdo){
	//private void registrarRtdo(String rtdo){
		String sentenciaSQL = "INSERT INTO resultados (id_sesion, id_tema, resultado) VALUES ("+idSesionActual+", "+idTemaActual+", '"+rtdo+"')";
		try {
			conectorbd.consulta(sentenciaSQL);
		} catch (SQLException e) {
			System.err.println(e.getMessage());
		}
	}
	
	/**
	 * M�todo que cuenta los votos de una votaci�n y los devuelve en un arreglo
	 * @return arreglo de enteros con los resultados
	 */
	private int[] procesarRtdos(){
		int[] resultados = new int[3];
		String sentenciaSQL;
		ResultSet rs;
		try{
			
			sentenciaSQL="SELECT COUNT(*) FROM votos_por_tema WHERE id_sesion="+idSesionActual+" AND id_tema="+idTemaActual+" AND voto='"+Mensaje.VOTO_POSITIVO+"';";
			rs = conectorbd.consulta(sentenciaSQL);
			rs.next();
			resultados[POSITIVOS] = rs.getInt("COUNT(*)");
			
			sentenciaSQL="SELECT COUNT(*) FROM votos_por_tema WHERE id_sesion="+idSesionActual+" AND id_tema="+idTemaActual+" AND voto='"+Mensaje.VOTO_NEGATIVO+"';";
			rs = conectorbd.consulta(sentenciaSQL);
			rs.next();
			resultados[NEGATIVOS] = rs.getInt("COUNT(*)");
			
			sentenciaSQL="SELECT COUNT(*) FROM votos_por_tema WHERE id_sesion="+idSesionActual+" AND id_tema="+idTemaActual+" AND voto='"+Mensaje.VOTO_ABSTENCION+"';";
			rs = conectorbd.consulta(sentenciaSQL);
			rs.next();
			resultados[ABSTENCION] = rs.getInt("COUNT(*)");
			
		}catch(SQLException e){
			System.err.println(e.getMessage());
		};
		return resultados;
	}
	
	/**
	 * M�todo que determina si un dado cliente se encuentra habilitado para votar. 
	 * Se realizan 2 verificaciones:
	 * 	- Que en la bd figure como habilitado.
	 * 	- Que no haya otro cliente conectado a la vez con el mismo fingerprint.
	 * 	
	 * @param fingerprint
	 * @return true si est� habilitado, false caso contrario.
	 */
	private boolean cliente_habilitado_a_conectarse(String fingerprint){
		boolean esta_habilitado= mapeoClientesAndroid.get(fingerprint)==null;
		 
		try {
			String sentenciaSQL="SELECT habilitado FROM clientes WHERE clave_publica = '"+fingerprint+"';";
			ResultSet rs = conectorbd.consulta(sentenciaSQL);
			if(rs.next()){
				String habilitado = rs.getString("habilitado");
				esta_habilitado = esta_habilitado && habilitado.equals("si");
			}else{ // no figura un cliente en la bd con el fingerprint recibido
				return false;
			}
			
		} catch (SQLException e) {
			System.err.println(e.getMessage());
		}
		
		return esta_habilitado;
	}
	
	/**
	 * M�todo que realiza la verificaci�n de si todos los clientes, a�n conectados, votaron.
	 * @return true si todos votaron, caso contrario false.
	 */
	private boolean verificarTodosVotaron1() {
		boolean todos_votaron=true;
		for(ThreadServerAndroid thread: mapeoClientesAndroid.values()){
			if(thread.clienteEstaConectado()){ // <<<<<<<<<<<<<<<<<<<<< ver si esto realmente funciona bien o si vemos si isAlive()
				try {
					String sentenciaSQL="SELECT * FROM votos_por_tema WHERE id_sesion = "+idSesionActual+" AND id_tema = "+idTemaActual+" AND id_cliente = '"+thread.obtenerFingerprint()+"';";
					ResultSet rs = conectorbd.consulta(sentenciaSQL);
					if(!rs.next())
						return false; // uno no vot�...
				
				} catch (SQLException e) {
					System.err.println(e.getMessage());
				}
			}
		}
		return todos_votaron;
	}

	/**
	 * M�todo que verifica si todos los clientes conectados fueron notificados(utilizado en estado 3). [ver que es lo que se les notifica]
	 * @return true si todos fueron notificados, caso contrario false
	 */
	private boolean verificarTodosNotificados(){
		for(ThreadServerAndroid cliente: mapeoClientesAndroid.values()){
			Mensaje[] ultimoMsj = cliente.obtenerUltimoIntercambio();
			String tipoRta = ultimoMsj[ThreadServerAndroid.RESPUESTA].getTipo_msj();
			String tipoPregunta = ultimoMsj[ThreadServerAndroid.PREGUNTA].getTipo_msj(); 
			boolean no_fue_notificado = cliente.clienteEstaConectado() && (tipoPregunta.equals(Mensaje.TIPO_HAY_NUEVO_TEMA) && tipoRta.equals(Mensaje.TIPO_NO));
			if(no_fue_notificado)
				return false;
		}
		return true;
	}

	/**
	 * M�todo que verifica si todos los clientes conectados fueron notificados(utilizado en estado 4). [ver que es lo que se les notifica]
	 * @return true si todos fueron notificados, caso contrario false
	 */	
	private boolean verificarTodosNotificadosE4(){
		for(ThreadServerAndroid cliente: mapeoClientesAndroid.values()){// deber�a verificara solamente aquellos AUN CONECTADOS!!!!!!!!!!!!!!
			boolean fue_notificado =cliente.notificadoPorHayTema();
			if(!fue_notificado  && cliente.clienteEstaConectado()) // <<<<<<<<<<<<<<< era tan sencillo??????
				return false;
		}
		return true;
	}
	
	protected synchronized boolean verificarIMEI(String fingerprint, String imei_socket){
		
		try{
			String sentenciaSQL="SELECT imei FROM clientes WHERE clave_publica = '"+fingerprint+"';";
			ResultSet rs = conectorbd.consulta(sentenciaSQL);
			if(rs.next()){// no deber�a fallar porque ya se verifico previamente que existia
				String imei_bd = rs.getString("imei");
				if(imei_bd.equals("VACIO")){ // el imei a�n no fue registrado, as� que se inserta el recibido en la bd
					sentenciaSQL = "UPDATE clientes SET imei='"+imei_socket+"' WHERE clave_publica='"+fingerprint+"';";
					conectorbd.consulta(sentenciaSQL);
					log.writeLog(Log.INFO, "SERVIDOR", "Verificacion IMEI", "Se inserto el IMEI "+imei_socket+" para el cliente "+fingerprint);	
					return true;
				}else{
					return imei_bd.equals(imei_socket);
				}
				
			}else{ // no figura un cliente en la bd con el fingerprint recibido
				return false;
			}
		}catch(SQLException e){
			System.err.println(e.getMessage());
			return false;
		}
		
	}
}